import java.awt.Point;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;

import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;

import com.speljohan.rsbot.accessors.NPC;
import com.speljohan.rsbot.bot.Bot;
import com.speljohan.rsbot.script.Constants;
import com.speljohan.rsbot.script.Methods;
import com.speljohan.rsbot.script.Script;
import com.speljohan.rsbot.script.wrappers.RSCharacter;
import com.speljohan.rsbot.script.wrappers.RSInterface;
import com.speljohan.rsbot.script.wrappers.RSItemTile;
import com.speljohan.rsbot.script.wrappers.RSNPC;
import com.speljohan.rsbot.script.wrappers.RSTile;

//Calculations. canReach(int startX, int startY, int destX, int destY, boolean isObject)
public class TehFighter extends Script {

	JFrame f;
	JTable t;

	boolean name = false;
	String[] names;
	boolean lg;
	int[] ids;

	int[] pi;
	String[] pn;
	int pmd;
	boolean bury;

	long startTime;
	RSTile startingLoc;

	int md = 20;
	boolean cw;
	int[] orig;

	int minHP = 0;
	int maxHP = 0;
	int nextEat = 0;

	long lastKill;
	Hashtable<String, String> kills;

	boolean le = false;
	int[] fi = { 1895, 1893, 1891, 13433, 319, 315, 2140, 2142, 2309, 347, 355,
			333, 329, 361, 379, 365, 371, 7946, 2299, 2297, 385, 1901, 1899,
			1897, 7934, 10476, 373 };
	int[] fh = { 4, 4, 4, 1, 1, 3, 3, 3, 5, 5, 6, 7, 9, 10, 12, 13, 14, 16, 9,
			9, 20, 5, 5, 5, 10, 3, 14 };

	public boolean clickItem(final int itemID) {
		if (getCurrentTab() != Constants.TAB_INVENTORY
				&& !RSInterface.getInterface(Constants.INTERFACE_BANK)
						.isValid()
				&& !RSInterface.getInterface(Constants.INTERFACE_STORE)
						.isValid()) {
			openTab(Constants.TAB_INVENTORY);
		}
		final int[] items = getInventoryArray();
		int possible = -1;
		for (int i = 0; i < items.length; i++) {
			if (items[i] == itemID) {
				possible = i;
			}
		}
		if (possible == -1) {
			return false;
		}
		final Point t = getInventoryItemPoint(possible);
		clickMouse(t, 5, 5, true);
		return true;
	}

	private boolean clickNPC(final RSNPC npc, final String action) {
		try {
			final int hoverRand = random(8, 13);
			for (int i = 0; i < hoverRand; i++) {
				final Point screenLoc = npc.getScreenLocation();
				if (!pointOnScreen(screenLoc)) {
					return false;
				}

				moveMouse(screenLoc, 15, 15);

				final java.util.List<String> menuItems = getMenuItems();
				if (menuItems.isEmpty() || menuItems.size() <= 1) {
					continue;
				}
				if (menuItems.get(0).toLowerCase().contains(
						npc.getName().toLowerCase())) {
					clickMouse(true);
					return true;
				} else {
					for (int a = 1; a < menuItems.size(); a++) {
						if (menuItems.get(a).toLowerCase().contains(
								npc.getName().toLowerCase())) {
							clickMouse(false);
							return atMenu(action);
						}
					}
				}
			}

		} catch (final Exception e) {
			System.out.print("clickNPC(RSNPC, String) error: " + e);
			return false;
		}
		return false;
	}

	public void equip() throws Exception {
		final int[] equip = getEquipmentArray();
		for (int i = 0; i < orig.length; i++) {
			if (orig[i] != equip[i]) {
				if (inventoryContains(orig[i])) {
					clickItem(orig[i]);
				}
			}

		}
	}

	@Override
	public RSTile[] fixPath(final RSTile[] path) {
		final ArrayList<RSTile> newPath = new ArrayList<RSTile>();
		for (int i = 0; i < path.length - 1; i++) {
			final RSTile[] temp1 = fixPath2(path[i], path[i + 1]);
			for (final RSTile element : temp1) {
				newPath.add(element);
			}
		}
		return newPath.toArray(new RSTile[newPath.size()]);
	}

	@Override
	public RSTile[] fixPath2(int startX, int startY, final int destinationX,
			final int destinationY) { // most credits to aftermath
		double dx, dy;
		final ArrayList<RSTile> list = new ArrayList<RSTile>();
		list.add(new RSTile(startX, startY));
		while (Math.hypot(destinationY - startY, destinationX - startX) > 8) {
			dx = destinationX - startX;
			dy = destinationY - startY;
			final int gamble = random(14, 17);
			while (Math.hypot(dx, dy) > gamble) {
				dx *= .95;
				dy *= .95;
			}
			startX += (int) dx;
			startY += (int) dy;
			list.add(new RSTile(startX, startY));
		}
		list.add(new RSTile(destinationX, destinationY));
		return list.toArray(new RSTile[list.size()]);
	}

	@Override
	public RSTile[] fixPath2(final RSTile tile) {
		return fixPath2(getMyPlayer().getLocation().getX(), getMyPlayer()
				.getLocation().getY(), tile.getX(), tile.getY());
	}

	@Override
	public RSTile[] fixPath2(final RSTile tile, final RSTile tile2) {
		return fixPath2(tile.getX(), tile.getY(), tile2.getX(), tile2.getY());
	}

	@Override
	public String getAuthor() {
		return "Mike_";
	}

	@Override
	public String getName() {
		return "TehFighter (Any Monster)";
	}

	@Override
	public RSNPC getNearestFreeNPCByID(final int... npcIds) {
		int Dist = 555;
		RSNPC closest = null;
		final int[] validNPCs = Bot.getClient().getNPCIndexArray();
		final NPC[] npcs = Bot.getClient().getNPCArray();

		for (final int element : validNPCs) {
			if (npcs[element] == null) {
				continue;
			}
			final RSNPC Monster = new RSNPC(npcs[element]);
			try {
				for (final int id : npcIds) {
					if (id != Monster.getID() || Monster.isInCombat()) {
						continue;
					}
					final int distance = distanceTo(Monster);
					if (distance < Dist
							&& Methods.distanceBetween(Monster.getLocation(),
									startingLoc) < md
							&& canReach(Monster.getLocation(), false)) {
						Dist = distance;
						closest = Monster;
					}
				}
			} catch (final Exception e) {
			}
		}
		return closest;
	}

	@Override
	public RSNPC getNearestFreeNPCByName(final String... npcNames) {
		int Dist = 500;
		RSNPC closest = null;
		final int[] validNPCs = Bot.getClient().getNPCIndexArray();
		final NPC[] npcs = Bot.getClient().getNPCArray();

		for (final int element : validNPCs) {
			if (npcs[element] == null) {
				continue;
			}
			final RSNPC Monster = new RSNPC(npcs[element]);
			try {
				for (final String name : npcNames) {
					if (name == null || Monster.getHPPercent() == 0
							|| !name.equalsIgnoreCase(Monster.getName())
							|| Monster.isInCombat()) {
						continue;
					}
					final int distance = distanceTo(Monster);
					if (distance < Dist
							&& Methods.distanceBetween(Monster.getLocation(),
									startingLoc) < md
							&& canReach(Monster.getLocation(), false)) {
						Dist = distance;
						closest = Monster;
					}
				}
			} catch (final Exception e) {
				e.printStackTrace();
			}
		}
		return closest;
	}

	public RSTile getNearestItemByID(final int id, final int range) {
		final int pX = getMyPlayer().getLocation().getX();
		final int pY = getMyPlayer().getLocation().getY();
		final int minX = pX - range;
		final int minY = pY - range;
		final int maxX = pX + range;
		final int maxY = pY + range;
		int index = -1;
		RSTile tileTemp = null;
		for (int x = minX; x <= maxX; x++) {
			for (int y = minY; y <= maxY; y++) {
				final RSItemTile[] items = getGroundItemsAt(x, y);
				for (int i = 0; i < items.length; i++) {
					final RSItemTile item = items[i];
					final int iId = item.getItem().getID();
					if (iId == id
							&& (index == -1 || distanceTo(item) > distanceTo(items[index]))) {
						index = i;
						tileTemp = item;
					}
				}
			}
		}
		return tileTemp;
	}

	@Override
	public String getScriptCategory() {
		return "Combat";
	}

	@Override
	public String getScriptDescription() {
		String html = "<html>";
		html += "<style>i{font:9pt arial}</style>";
		html += "<body>";
		html += "<b>Type: </b><br>";
		html += "<input type=radio name=type value=id checked>Search By NPC ID</input><br>";
		html += "<input type=radio name=type value=name>Search By NPC Name</input><br>";
		html += "<b>Value: </b> <input type=text name=info value=\"\"></input> <i> -Seperate with a comma(\",\")</i><br>";
		html += "<b>MD: </b> <input type=text name=md value=20></input> <i> -Max distance from starting location (So you don't wander)</i><br>";
		html += "<input type=checkbox name=dw value=true> Disable Deathwalk</input><br>";
		html += "<hr><br>";
		html += "<input type=checkbox name=le value=\"true\">Logout if we run out of food.</input> <i>-If you don't have food, this will log you out at [MaxEat].<br>";
		html += "<b>Minumum Eating HP: </b> <input type=text name=mineat value=\"5\"></input> <i>-Will always eat below this number.</i><br>";
		html += "<b>Maximum Eating HP: </b> <input type=text name=maxeat value=\"10\"></input> <i>-Will never eat above this number.<br>";
		html += "<hr><br>";
		html += "<span style=\"font:8pt arial;\"><b>Pickup Instructions:</b> Include Arrow ID/Bone ID. Format: \"ID:Name\". Example: \"526:Bones\". Seperate with a comma (\",\")</span><br>";
		html += "<b>Pick Up Items: </b> <input type=text name=pickup value=\"\"><br>";
		html += "<b>Pickup MD: </b> <input type=text name=pmd value=1></input> <i> -Max distance from player to pickup items.</i><br>";
		html += "<hr><br>";
		html += "<span style=\"font:8pt arial;\">";
		html += "When eating, you will eat until your health is nearly full. For example, will if your food heals 7 and you have 93/99 hp it won't eat anymore... But if you had 92/99hp it would eat.<br>";
		html += "<b>Supported Foods: </b> Crayfish, Anchovies, Shrimp, Cooked Chicken, Cooked Meat, Bread, Herring, Mackerel, Trout, Salmon, Tuna, Cake, Lobster, Bass, Swordfish, Monkfish, Anchovy Pizza, Shark, Chocolate Cake, and Purple Sweets";
		html += "Any items in the pickup list with the word \"Bone\" in it will be burried.";
		html += "</span>";
		html += "</body>";
		html += "</html>";
		return html;
	}

	@Override
	public double getVersion() {
		return 1.0;
	}

	// //EDITED METHODS.////

	@Override
	public int loop() { // the majority of the script will go here.
		if (lg) {
			log("Logged in(hopefully). Please run the script again!");
			return -1;
		}
		try {
			int curHP = skills.getCurrentSkillLevel(Constants.STAT_HITPOINTS);
			final int nextFood = nextFood();
			if (le && curHP < nextFood && !inventoryContainsOneOf(fi)) {
				log("Out of food. Attempting to logout");
				if (logout()) {
					return -1;
				}
			}
			if (nextEat >= curHP && nextFood >= 0) {
				nextEat = random(minHP - 1, maxHP);
				for (int i = 0; i < 50; i++) {
					curHP = skills
							.getCurrentSkillLevel(Constants.STAT_HITPOINTS);
					final int food = nextFood();
					if (food == -1) {
						break;
					}
					if (clickItem(fi[food])) {
						wait(random(100, 350));
					}
				}
			}

			equip();
			int dist = 0;
			if (getDestination() != null) {
				dist = distanceTo(getDestination());
			}
			if (dist > 0 && !getMyPlayer().isMoving() && random(1, 10) == 5) { // bug
				// fix
			} else if (getMyPlayer().isMoving() || dist > 0) {
				return random(50, 100);
			}
			if (distanceTo(startingLoc) > md) {
				if (cw) {
					walkPathMM(randomizePath(
							fixPath(generateProperPath(startingLoc)), 2, 2), 17);
				} else if (distanceTo(startingLoc) > md + 20) {
					return -1;
				} else {
					walkPathMM(randomizePath(fixPath2(startingLoc), 2, 2), 17);
				}
			}
			final RSCharacter interacting = getMyPlayer().getInteracting();
			if (interacting != null) {
				if (interacting instanceof RSNPC) {
					final RSNPC inter = (RSNPC) interacting;
					if (inter.getHPPercent() == 0) {
						final long now = System.currentTimeMillis();
						if (now - lastKill > 3000) {
							// log("");
							lastKill = now;
							int killsAmt = 1;
							log("temp");
							if (kills.containsKey("(" + inter.getName().trim()
									+ ")s")) {
								log("temp2");
								killsAmt = Integer.parseInt(kills.get("("
										+ inter.getName().trim() + ")s")) + 1;
								kills.remove("(" + inter.getName().trim()
										+ ")s");
							}
							kills.put("(" + inter.getName().trim() + ")s",
									killsAmt + "");
							// log("");
							time();

						}
					} else {
						return random(50, 100);
					}
				}
			}
			if (getMyPlayer().getAnimation() != -1) {
				return random(50, 100);
			}
			if (!isInventoryFull() && pickup()) {
				return random(200, 300);
			}
			RSNPC monster;
			if (name) {
				monster = getNearestFreeNPCByName(names);
			} else {
				monster = getNearestFreeNPCByID(ids);
			}

			if (monster == null) {
				if (random(0, 10) == 5) {
					walkPathMM(randomizePath(fixPath2(startingLoc), 2, 2), 17);
				}
				return random(50, 100);
			}
			if (clickNPC(monster, "Attack " + monster.getName())) {
				return random(50, 100);
			}
			if (clickNPC(monster, "Attack " + monster.getName())) {
				return random(50, 100);
			}
			if (clickNPC(monster, "Attack " + monster.getName())) {
				return random(50, 100);
			}
			if (distanceTo(monster) > 7) {
				walkPathMM(
						randomizePath(fixPath2(monster.getLocation()), 2, 2),
						17);
			} else if (random(0, 20) == 5
					|| !tileOnScreen(monster.getLocation())) {
				turnToCharacter(monster);
			}
		} catch (final Exception e) {
			e.printStackTrace();
		}
		return random(100, 150);
	}

	public int nextFood() {
		int index = -1;
		final int totalHP = skills.getRealSkillLevel(Constants.STAT_HITPOINTS);
		final int curHP = skills.getCurrentSkillLevel(Constants.STAT_HITPOINTS);
		for (int i = 0; i < fi.length; i++) {
			if (inventoryContains(fi[i]) && (index == -1 || fh[i] > fh[index])
					&& (fh[i] + curHP <= totalHP || curHP < minHP)) {
				index = i;
			}
		}
		return index;
	}

	@Override
	public void onFinish() {
	}

	@Override
	public boolean onStart(final Map<String, String> args) {
		lg = false;

		lastKill = -1;
		le = false;
		kills = new Hashtable<String, String>();
		kills.put("NAME", "AMOUNT");
		kills.put("Time Running", "0 seconds");
		orig = getEquipmentArray();
		cw = true;
		name = false;
		cw = false;
		md = 20;
		try {
			startTime = System.currentTimeMillis();
			final String mdTemp = args.get("md").trim();
			if (mdTemp == null || mdTemp.length() <= 0) {
				md = -1;
			} else {
				md = Integer.parseInt(mdTemp);
			}
			pmd = Integer.parseInt(args.get("pmd").trim());
			final String pickuptemp = args.get("pickup").trim();
			log(pickuptemp);
			if (pickuptemp == null || pickuptemp.length() <= 0) {
				pi = null;
				pn = null;
				log("No pickup");
			} else {

				final String[] pickuptemp2 = pickuptemp.split(",");
				pi = new int[pickuptemp2.length];
				pn = new String[pickuptemp2.length];
				for (int i = 0; i < pickuptemp2.length; i++) {
					log(pickuptemp2[i]);
					final String[] ptemp = pickuptemp2[i].split(":");
					ptemp[0] = ptemp[0].trim();
					ptemp[1] = ptemp[1].trim();
					pi[i] = Integer.parseInt(ptemp[0]);
					pn[i] = ptemp[1];
					log("Item: " + pn[i] + ". ID: " + pi[i]);
				}
			}
			minHP = Integer.parseInt(args.get("mineat").trim());
			maxHP = Integer.parseInt(args.get("maxeat").trim());
			if (args.get("le") != null
					&& args.get("le").equalsIgnoreCase("true")) {
				le = true;
			}
			final int totalHP = skills
					.getRealSkillLevel(Constants.STAT_HITPOINTS);
			if (minHP == 0 || maxHP > totalHP) {
				log("Invalid HP args.");
			}
			nextEat = random(minHP - 1, maxHP);
			final RSTile tempTile = getMyPlayer().getLocation();
			if (!isLoggedIn() || tempTile == null || tempTile.getX() == 23
					&& tempTile.getY() == 23) {
				log("You must be logged in to begin this script. We will log you in, run this again afterwards.");
				lg = true;
				return true;
			}
			startingLoc = tempTile;
			RSTile[] walkTest = null;
			generateProperPath(3212, 3429);
			if (args.get("dw") == null) {
				walkTest = generateProperPath(3212, 3429);
				if (getPlane() != 0) {
					log("Deathwalk disabled because you are not on the first floor");
					cw = false;
				} else if (Methods.distanceBetween(walkTest[0], walkTest[1]) > 50) {
					log("Cannot generate path");
					log("DIST: "
							+ Methods.distanceBetween(walkTest[0], walkTest[1]));
					cw = false;
				} else {
					String s1 = "";
					for (final RSTile element : walkTest) {
						s1 += element.toString();
					}
					log("Path Generated  " + s1);
					cw = true;
				}
			}
			if (args.get("type").equalsIgnoreCase("id")) {
				final String temp = args.get("info");
				final String[] temp2 = temp.split(",");
				ids = new int[temp2.length];
				for (int i = 0; i < temp2.length; i++) {
					temp2[i] = temp2[i].trim();
					ids[i] = Integer.parseInt(temp2[i]);
					log(temp2[i]);
				}
			} else {
				final String temp = args.get("info");
				names = temp.split(",");
				for (int i = 0; i < names.length; i++) {
					names[i] = names[i].trim();
				}
				name = true;
			}
			showGUI();
			return true;
		} catch (final Exception e) {
			e.printStackTrace();
		}
		log("Some kind of error.");
		return false;
	}

	public boolean pickup() {
		if (pn == null || pi == null) {
			return false;
		}
		for (int i = 0; i < pi.length; i++) {
			if (pn[i].toLowerCase().contains("bone")) {
				if (inventoryContains(pi[i])) {
					clickItem(pi[i]);
					wait(random(700, 1500));
					return true;
				}
			}
			final RSTile t = getNearestItemByID(pi[i], pmd);
			if (t == null) {
				continue;
			}
			if (!atTile(t, "Take " + pn[i])) {
				turnToTile(t);
			}
			if (tileOnScreen(t)) {
				return true;
			}
		}
		return false;
	}

	// ///GUI bullshit
	public void showGUI() {
		f = new JFrame(getName() + " v" + getAuthor() + " -By " + getAuthor());
		t = new JTable(1, 2);
		f.setAlwaysOnTop(true);
		f
				.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
		f.getContentPane().add(t);
		f.pack();
		f.setSize(200, 400);
		f.setVisible(true);
	}

	public void time() {
		long millis = System.currentTimeMillis() - startTime;
		final long hours = millis / (1000 * 60 * 60);
		millis -= hours * 1000 * 60 * 60;
		final long minutes = millis / (1000 * 60);
		millis -= minutes * 1000 * 60;
		final long seconds = millis / 1000;
		String hoursString = "";
		String minutesString = "";
		String secondsString = seconds + "";
		String type = "seconds";
		if (minutes > 0) {
			minutesString = minutes + ":";
			type = "minutes";
		}
		if (hours > 0) {
			hoursString = hours + ":";
			type = "hours";
		}
		if (minutes < 10 && hours > 0) {
			minutesString = "0" + minutesString;
		}
		if (seconds < 10 && minutes > 0) {
			secondsString = "0" + secondsString;
		}
		kills.remove("Time Running");
		kills.put("Time Running", "" + hoursString + minutesString
				+ secondsString + " " + type + "");
		updateTable();
	}

	public void updateTable() {
		t.setModel(new AbstractTableModel() {
			private static final long serialVersionUID = 1L;

			public int getColumnCount() {
				return 2;
			}

			@Override
			public String getColumnName(final int column) {
				if (column == 0) {
					return "Animal";
				} else {
					return "Sound";
				}
			}

			private String getKey(final int a_index) {
				String retval = "";
				final Enumeration<String> e = kills.keys();
				for (int i = 0; i < a_index + 1; i++) {
					retval = e.nextElement();
				} // for

				return retval;
			}

			public int getRowCount() {
				return kills.size();
			}

			public Object getValueAt(final int rowIndex, final int columnIndex) {
				if (columnIndex == 0) {
					return getKey(rowIndex);
				} else {
					return kills.get(getKey(rowIndex));
				} // if-else

			}

		});
		// f.pack();
	}

}